﻿using Maticsoft.Common;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using TencentYoutuYun.SDK.Csharp;
using TencentYoutuYun.SDK.Csharp.Model;

namespace MvcCodeNotes.Controllers
{
    public class FaceController : Controller
    {
        //
        // GET: /Face/

        public ActionResult Index()
        {
            return View();
        }

        public ActionResult FaceShape()
        {
            return View();
        }

        [HttpPost]
        [ValidateInput(false)]
        public JsonResult WxUpload(FormCollection collection)
        {

            DetectfaceViewModel backStr = new DetectfaceViewModel();
            var obj = new object();
            HttpFileCollectionBase files = Request.Files;
            if (files.Count > 0)
            {
                HttpPostedFileBase thumbnail = files[0];
                string base64 = ImgBase64(thumbnail);
                backStr = FaceApi.api_detectface_base64(base64);

                //图片保存到本地
                //string directoryPath = Server.MapPath("~/images/face/temp/");
                //if (!Directory.Exists(directoryPath))//不存在这个文件夹就创建这个文件夹 
                //{
                //    Directory.CreateDirectory(directoryPath);
                //}
                //string fileName = thumbnail.FileName;
                //string fileExt = Path.GetExtension(fileName).ToLower();
                //path = DateTime.Now.ToString("yyyMMddHHmmss") + Assistant.GenerateRdm(3, false) + fileExt;
                //string savepath = directoryPath + path;
                //Image img = fileToImage(thumbnail);
                ////如果图片大于100k，进行压缩
                //if (thumbnail.ContentLength > 1024 * 100 && !FaceApi.ReduceImage(img, savepath, 50))
                //{
                //    thumbnail.SaveAs(savepath);
                //}
                //else
                //{
                //    thumbnail.SaveAs(savepath);
                //}

            }

            return Json(backStr);

        }

        /// <summary>
        /// 人脸检测
        /// </summary>
        /// <param name="collection"></param>
        /// <returns></returns>
        [HttpPost]
        [ValidateInput(false)]
        public JsonResult FaceDetect(FormCollection collection)
        {

            string path = "";
            string state = "fail";
            var obj = new object();
            HttpPostedFileBase thumbnail = Request.Files["imgFile"];
            if (thumbnail != null && thumbnail.FileName != "")
            {
                string directoryPath = Server.MapPath("~/images/face/temp/");
                if (!Directory.Exists(directoryPath))//不存在这个文件夹就创建这个文件夹 
                {
                    Directory.CreateDirectory(directoryPath);
                }
                string fileName = thumbnail.FileName;
                string fileExt = Path.GetExtension(fileName).ToLower();
                path = DateTime.Now.ToString("yyyMMddHHmmss") + Assistant.GenerateRdm(3, false) + fileExt;
                string savepath = directoryPath + path;
             
                Image img = fileToImage(thumbnail);
                //如果图片大于100k，进行压缩
                if (thumbnail.ContentLength > 1024 * 100 && !FaceApi.ReduceImage(img, savepath, 50))
                {
                    thumbnail.SaveAs(savepath);
                }
                else
                {
                    thumbnail.SaveAs(savepath);
                }
                thumbnail.SaveAs(savepath);

                ApiFaceshapeViewModel backStr = FaceApi.api_faceshape_path(savepath);
                bool isSucc = false;
              
                if (backStr.errorcode == 0)
                {
                    StringBuilder sb = new StringBuilder();
                    if (backStr.face_shape.Count > 0)
                    {
                        isSucc = true;
                        List<FaceShapeModel> faceList = backStr.face_shape;
                        for(int i=0;i<faceList.Count&&i<2;i++)
                        {
                            FaceShapeModel face = backStr.face_shape[i];
                            List<PointModel> pointList = new List<PointModel>();
                            //先右眼眉后左，否则不是封闭的图形
                            if (face.face_profile.Count > 0)
                                pointList.AddRange(face.face_profile);
                            if (face.right_eyebrow.Count > 0)
                                pointList.AddRange(face.right_eyebrow.Skip(4).Reverse());
                            if (face.left_eyebrow.Count > 0)
                                pointList.AddRange(face.left_eyebrow.Skip(4));


                            //获取最小的x、y值和最大的x、y值
                            int minX = -1, minY = -1, maxX = 0, maxY = 0;
                            foreach (var point in pointList)
                            {
                                if (minX == -1 || point.x < minX)
                                    minX = point.x;
                                if (minY == -1 || point.y < minY)
                                    minY = point.y;
                                if (point.x > maxX)
                                    maxX = point.x;
                                if (point.y > maxY)
                                    maxY = point.y;
                            }
                            //把脸部图片保存下来
                            int width = maxX - minX;
                            int height = maxY - minY;
                            Bitmap bmp = new Bitmap(width, height);
                            Bitmap back = new Bitmap(img);

                            for (int h = 0; h < height; h++)
                            {
                                for (int w = 0; w < width; w++)
                                {
                                    //进一步判断该点是否在脸部的轮廓内
                                    int x = minX + w;
                                    int y = minY + h;
                                    PointModel point = new PointModel() { x = x, y = y };
                                    if (IsInPolygon2(point, pointList))
                                    {
                                        Color colorNew = back.GetPixel(x, y);
                                        bmp.SetPixel(w, h, colorNew);
                                    }

                                }
                            }
                            string savepath_face = directoryPath + DateTime.Now.ToString("yyyMMddHHmmss") + Assistant.GenerateRdm(3, false) + "_faceshape"+i+".png";
                            bmp.Save(savepath_face);

                        }
                        
                    }

                }
                if (isSucc)
                {
                    obj = new
                    {
                        state = "success",
                        picSrc = path,
                        back = backStr
                    };
                }
                else
                {
                    obj = new
                    {
                        state = "error",
                        content = backStr
                    };
                }


                ////人脸检测，并把人脸保存成图片
                //DetectfaceViewModel backStr = FaceApi.api_detectface_path(savepath);
                //if (backStr.errorcode == 0)
                //{
                //    StringBuilder sb = new StringBuilder();
                //    if (backStr.face.Count > 0)
                //    {
                //        FaceModel face = backStr.face[0];
                //        int width=Convert.ToInt32(face.width);
                //        int height=Convert.ToInt32(face.height);
                //        Bitmap back = new Bitmap(img);
                //        Bitmap bmp = new Bitmap(width, height);
                //        for (int h = 0; h < height; h++)
                //        {
                //            for (int w = 0; w < width; w++)
                //            {
                //                int x = Convert.ToInt32(face.x);
                //                int y = Convert.ToInt32(face.y);
                //                Color colorNew = back.GetPixel(x+w, y+h);
                //                bmp.SetPixel(w, h, colorNew);                              
                //            }
                //        }
                //        bmp.Save(savepath_face);
                //    }
                //    obj = new
                //    {
                //        state = "success",
                //        picSrc = path,
                //        back=backStr,
                //        content = sb.ToString()
                //    };
                //}
                //else
                //{
                //    obj = new
                //    {
                //        state = "error",
                //        content = backStr
                //    };
                //}
            }
            else
            {
                obj = new
                {
                    state = "error",
                    picSrc = path
                };
            }

            return Json(obj);

        }

        /// <summary>
        /// 五官定位
        /// </summary>
        /// <param name="collection"></param>
        /// <returns></returns>
        [HttpPost]
        [ValidateInput(false)]
        public JsonResult FaceShape(FormCollection collection)
        {

            string path = "";            
            var obj = new object();
            HttpPostedFileBase thumbnail = Request.Files["imgFile"];
            if (thumbnail != null && thumbnail.FileName != "")
            {
                string directoryPath = Server.MapPath("~/images/face/temp/");
                if (!Directory.Exists(directoryPath))//不存在这个文件夹就创建这个文件夹 
                {
                    Directory.CreateDirectory(directoryPath);
                }
                string fileName = thumbnail.FileName;
                string fileExt = Path.GetExtension(fileName).ToLower();
                path = DateTime.Now.ToString("yyyMMddHHmmss") + Assistant.GenerateRdm(3, false) + fileExt;
                string savepath = directoryPath + path;

                Image img = fileToImage(thumbnail);
                ////如果图片大于100k，进行压缩
                //if (thumbnail.ContentLength > 1024 * 100 && !FaceApi.ReduceImage(img, savepath, 50))
                //{
                //    thumbnail.SaveAs(savepath);
                //}
                //else
                //{
                //    thumbnail.SaveAs(savepath);
                //}
                thumbnail.SaveAs(savepath);

                ApiFaceshapeViewModel backStr = FaceApi.api_faceshape_path(savepath);
                bool isSucc = false;
                List<object> retFace = new List<object>();
                if (backStr.errorcode == 0)
                {
                    StringBuilder sb = new StringBuilder();
                    if (backStr.face_shape.Count > 0)
                    {
                        isSucc = true;
                        List<FaceShapeModel> faceList = backStr.face_shape;
                        for (int i = 0; i < faceList.Count && i < 2; i++)
                        {
                            FaceShapeModel face = backStr.face_shape[i];
                            List<PointModel> pointList = new List<PointModel>();
                            //先右眼眉后左，否则不是封闭的图形，去眉毛的下沿，否则有眼镜不好看
                            if (face.face_profile.Count > 0)
                                pointList.AddRange(face.face_profile);

                            if (face.right_eyebrow.Count > 0)
                                pointList.AddRange(face.right_eyebrow.Take(5));
                            if (face.left_eyebrow.Count > 0)
                                pointList.AddRange(face.left_eyebrow.Skip(5).Reverse());

                            //if (face.right_eyebrow.Count > 0)
                            //    pointList.AddRange(face.right_eyebrow.Skip(4).Reverse());
                            //if (face.left_eyebrow.Count > 0)
                            //    pointList.AddRange(face.left_eyebrow.Skip(4));


                            //获取最小的x、y值和最大的x、y值
                            int minX = -1, minY = -1, maxX = 0, maxY = 0;
                            foreach (var point in pointList)
                            {
                                if (minX == -1 || point.x < minX)
                                    minX = point.x;
                                if (minY == -1 || point.y < minY)
                                    minY = point.y;
                                if (point.x > maxX)
                                    maxX = point.x;
                                if (point.y > maxY)
                                    maxY = point.y;
                            }
                            //把脸部图片保存下来
                            int width = maxX - minX;
                            int height = maxY - minY;
                            Bitmap bmp = new Bitmap(width, height);
                            Bitmap back = new Bitmap(img);

                            for (int h = 0; h < height; h++)
                            {
                                for (int w = 0; w < width; w++)
                                {
                                    //进一步判断该点是否在脸部的轮廓内
                                    int x = minX + w;
                                    int y = minY + h;
                                    PointModel point = new PointModel() { x = x, y = y };
                                    if (IsInPolygon2(point, pointList))
                                    {
                                        Color colorNew = back.GetPixel(x, y);
                                        bmp.SetPixel(w, h, colorNew);
                                    }

                                }
                            }
                            string savepath_face =  DateTime.Now.ToString("yyyMMddHHmmss") + Assistant.GenerateRdm(3, false) + "_faceshape" + i + ".png";
                            bmp.Save(directoryPath + savepath_face);

                            object faceModel = new
                            {
                                x = minX,
                                y = minY,
                                width = width,
                                height = height,
                                src = savepath_face
                            };
                            retFace.Add(faceModel);
                        }
                    }
                }
                if (isSucc)
                {
                    obj = new
                    {
                        state = "success",
                        picSrc = path,
                        retFace=retFace,
                        back = backStr
                    };
                }
                else
                {
                    obj = new
                    {
                        state = "error",
                        content = backStr
                    };
                }     
            }
            else
            {
                obj = new
                {
                    state = "error",
                    picSrc = path
                };
            }

            return Json(obj);

        }

        /// <summary>  
        /// 判断点是否在多边形内.  
        /// ----------原理----------  
        /// 注意到如果从P作水平向左的射线的话，如果P在多边形内部，那么这条射线与多边形的交点必为奇数，  
        /// 如果P在多边形外部，则交点个数必为偶数(0也在内)。  
        /// 所以，我们可以顺序考虑多边形的每条边，求出交点的总个数。还有一些特殊情况要考虑。假如考虑边(P1,P2)，  
        /// 1)如果射线正好穿过P1或者P2,那么这个交点会被算作2次，处理办法是如果P的从坐标与P1,P2中较小的纵坐标相同，则直接忽略这种情况  
        /// 2)如果射线水平，则射线要么与其无交点，要么有无数个，这种情况也直接忽略。  
        /// 3)如果射线竖直，而P0的横坐标小于P1,P2的横坐标，则必然相交。  
        /// 4)再判断相交之前，先判断P是否在边(P1,P2)的上面，如果在，则直接得出结论：P再多边形内部。  
        /// </summary>  
        /// <param name="checkPoint">要判断的点</param>  
        /// <param name="polygonPoints">多边形的顶点</param>  
        /// <returns></returns>  
        public static bool IsInPolygon2(PointModel checkPoint, List<PointModel> polygonPoints)
        {
            int counter = 0;
            int i;
            double xinters;
            PointModel p1, p2;
            int pointCount = polygonPoints.Count;
            p1 = polygonPoints[0];
            for (i = 1; i <= pointCount; i++)
            {
                p2 = polygonPoints[i % pointCount];
                if (checkPoint.y > Math.Min(p1.y, p2.y)//校验点的Y大于线段端点的最小Y  
                    && checkPoint.y <= Math.Max(p1.y, p2.y))//校验点的Y小于线段端点的最大Y  
                {
                    if (checkPoint.x <= Math.Max(p1.x, p2.x))//校验点的X小于等线段端点的最大X(使用校验点的左射线判断).  
                    {
                        if (p1.y != p2.y)//线段不平行于X轴  
                        {
                            xinters = (checkPoint.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
                            if (p1.x == p2.x || checkPoint.x<= xinters)
                            {
                                counter++;
                            }
                        }
                    }

                }
                p1 = p2;
            }

            if (counter % 2 == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        private bool isInFaceshape(List<PointModel> pointList, PointModel point)
        {
            bool isIn = false;
            //1、先找到最近的y值，获取此y值对应的minX和maxX
            //2、如果此点的x值在minX和maxX之间，则在脸部内
            int destY = -1;
            int minY = -1;
            int minX = -1;
            int maxX = -1;
            foreach (var TempPoint in pointList)
            {
                int deltaY = Math.Abs(TempPoint.y - point.y);
                if (minY == -1 || minY > deltaY)
                {
                    minY = deltaY;
                    destY = TempPoint.y;
                }
            }

            foreach (var TempPoint in pointList)
            {
                if (Math.Abs(TempPoint.y - destY) < 3)
                {
                    if (minX == -1 || minX > TempPoint.x)
                    {
                        minX = TempPoint.x;
                    }
                    if (maxX == -1 || maxX < TempPoint.x)
                    {
                        maxX = TempPoint.x;
                    }
                }
            }
            if (point.x >= minX && point.x <= maxX)
                isIn = true;

            return isIn;
        }

        private string ImgBase64(HttpPostedFileBase file)
        {
            string img64 = "";
            if (file != null && file.ContentLength > 0)
            {
                MemoryStream ms = new MemoryStream();
                string file_etx = Path.GetExtension(file.FileName).ToLower();
                System.Drawing.Image img = new Bitmap(file.InputStream);
                switch (file_etx)
                {
                    case ".jpg":
                        img.Save(ms, ImageFormat.Jpeg);
                        break;
                    case ".png":
                        img.Save(ms, ImageFormat.Png);
                        break;
                    case ".gif":
                        img.Save(ms, ImageFormat.Gif);
                        break;
                    case ".bmp":
                        img.Save(ms, ImageFormat.Bmp);
                        break;
                    default:
                        img.Save(ms, ImageFormat.Jpeg);
                        break;

                }
                img64 = Convert.ToBase64String(ms.ToArray());
            }
            return img64;
        }

        private Image fileToImage(HttpPostedFileBase file)
        {
            MemoryStream ms = new MemoryStream();
            string file_etx = Path.GetExtension(file.FileName).ToLower();
            Image img = new Bitmap(file.InputStream);
            switch (file_etx)
            {
                case ".jpg":
                    img.Save(ms, ImageFormat.Jpeg);
                    break;
                case ".png":
                    img.Save(ms, ImageFormat.Png);
                    break;
                case ".gif":
                    img.Save(ms, ImageFormat.Gif);
                    break;
                case ".bmp":
                    img.Save(ms, ImageFormat.Bmp);
                    break;
                default:
                    img.Save(ms, ImageFormat.Jpeg);
                    break;
            }
            return img;
        }

    }
}
